#include <string.h>

#include <drivers.h>
#include <drivers/serial.h>

static size_t _serial_poll_read(struct device *dev,
									void *buffer,
									size_t size)
{
	int ret;
	size_t get_size;
	struct serial *serial;
	uint8_t *get_buffer = (uint8_t *)buffer;

	assert(dev != NULL);

	serial = container_of(dev, struct serial, parent);
	assert(serial != NULL);

	for(get_size = 0; get_size < size; ++get_size) {
		ret = serial->ops->getc(serial);
		if (ret < 0)
			break;

		*get_buffer = ret;

		++get_buffer;
	}

	return get_size;
}

static size_t _serial_poll_write(struct device *dev,
                                    const void *buffer,
                                    size_t size)
{
	int ret;
	size_t put_size;
	struct serial *serial;
	uint8_t *put_buffer = (uint8_t *)buffer;

	assert(dev != NULL);

	serial = container_of(dev, struct serial, parent);
	assert(serial != NULL);

	for(put_size = 0; put_size < size; ++put_size) {
		ret = serial->ops->putc(serial, put_buffer[put_size]);
		if (ret < 0)
			break;
	}

	return put_size;
}

static err_t serial_init(struct device *dev)
{
	struct serial *serial = container_of(dev, struct serial, parent);

	serial->ops->configure(serial, serial->configure);

	return 0;
}

static err_t serial_open(struct device *dev, uint16_t oflag)
{
	return 0;
}

static err_t serial_close(struct device *dev)
{
	return 0;
}

static size_t serial_read(struct device *dev, const size_t offset, void *buffer, size_t size)
{
	assert(dev != NULL);

	if (size == 0)
		return 0;

	return _serial_poll_read(dev, buffer, size);
}

static size_t serial_write(struct device *dev, size_t offset, const void *buffer, size_t size)
{
	assert(dev != NULL);

	if (size == 0)
		return 0;

	return _serial_poll_write(dev, buffer, size);
}

static err_t serial_control(struct device *dev, int cmd, void *args)
{
	return 0;
}

struct device_ops ops = {
	.init = serial_init,
	.open = serial_open,
	.write = serial_write,
	.read = serial_read,
	.close = serial_close,
	.control = serial_control,
};

err_t hw_serial_register(struct serial *serial, const char *name, void *private_data)
{
	err_t ret = 0;

	serial->parent.type = DEVICE_TYPE_CHAR;
	serial->parent.ops = &ops;
	serial->parent.private_data = private_data;
	ret = device_register(&serial->parent, name, DEVICE_FLAG_RW);

	return ret;
}
